﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using HK.Core.Network.Download.Data;
using HK.Core.Network.Download.Protocol.Control;
using HK.Core.Network.Download.Protocol.Data;
using HK.Core.Utils;
using HLogger = HK.Core.Logs.Logger;
using HThreader = HK.Core.Threader.Threader;

namespace HK.Core.Network.Download.Control
{
    /// <summary>
    /// 下载管理器
    /// </summary>
    public sealed class DownloadManager : HThreader, IDownloadManager
    {

#region Create

        /// <summary>
        /// 创建下载管理器
        /// </summary>
        /// <param name="iRetry">重试次数(默认:3)</param>
        /// <param name="iThreadNum">开启线程数(默认:10)</param>
        /// <param name="iMoveFile">移动文件标志位</param>
        /// <param name="iOptions">下载选项</param>
        /// <param name="iDecompressed">解压/解密回调</param>
        /// <returns>下载管理器</returns>
        public static DownloadManager Create(int iRetry = 3, int iThreadNum = 10, bool iMoveFile = false, 
            int iOptions = (int)DownloadOptions.ResFullDownload, DownloadDecompressed iDecompressed = null)
        {
            var manager = new DownloadManager();

            // 初始化
            manager.Init(iRetry, iThreadNum, iMoveFile, iOptions, iDecompressed);
            return manager;
        }

#endregion
        
        /// <summary>
        /// 保护构造函数
        /// </summary>
        private DownloadManager() {}
        
#region Events

        /// <summary>
        /// 事件：下载进度
        /// </summary>
        public DownloadProgress Progress { get; private set; } = null;
                
        /// <summary>
        /// 事件：下载失败
        /// </summary>
        public DownloadFailed Failed { get; private set; } = null;

        /// <summary>
        /// 下载器完成回调
        /// <para>* 单个下载目标完成回调.（包括成功与失败）</para>
        /// <param name="iState">下载状态</param>
        /// <param name="iDownloader">下载信息</param>
        /// <param name="iMessage">消息的具体内容</param>
        /// </summary>
        private IDownloadInfo OnDownloaderCompleted(DownloadState iState, IDownloader iDownloader, string iMessage = null)
        {
            State = iState;
            StateDes = iMessage;
            
            // Error("OnDownloaderCompleted():Index:{0} State:{1} - {2}", iDownloader.Index, iState, iDownloader.Target.FileName);
            
            if (DownloadState.Ok != iState)
            {
                // 下载失败
                Error("OnDownloaderCompleted():Failed!(State:{0} Message:{1}\nTarget:{2})",
                    iState, string.IsNullOrEmpty(iMessage) ? "-" : iMessage,
                    null == iDownloader?.Target ? "null" : iDownloader.Target.ToString());

                return null;
            }

            var target = iDownloader.Target;
            if (null != target && target.Valid)
            {
                // Error("OnDownloaderCompleted():Index:{0} - State:{1} - Id:{2}", iDownloader.Index, iState, target.Id);
                // 标记下载完成
                target.Downloaded = true;
                
                // 生成下载速度单元
                var speedUnit = DownloadSpeedUnit.Create(target.FileName, target.Size, UtilsTime.GetCurDateTimeTicks());
                Speed.AddSpeedPoint(speedUnit);
            }

            // 取得下一个下载目标
            var nextDstTarget = GetNextDownloadTarget();
            if(null == nextDstTarget || !nextDstTarget.Valid)
            {
                //Warning("OnDownloaderCompleted():GetNextDownloadTarget() Failed!");
                return null;
            }
            
            return nextDstTarget;
        }
        
#endregion

#region Params

        /// <summary>
        /// 已完成的下载列表
        /// </summary>
        private List<IDownloadInfo> _downloadTargets = new List<IDownloadInfo>();

        /// <summary>
        /// 初始化下载列表
        /// </summary>
        /// <param name="iTargets">下载目标列表</param>
        /// <returns>true:Ok; false:NG;</returns>
        private bool InitDownloadTargets(IList<IDownloadInfo> iTargets)
        {
            if (null == iTargets || 0 >= iTargets.Count)
            {
                Error("InitDownloadQueue():There is no download target exist!");
                return false;
            }

            var groupTargets = iTargets.GroupBy(iO => iO.FileName).ToList();
            var groupCount = groupTargets.Count;
            for (var idx = 0; idx < groupCount; ++idx)
            {
                // 按版本排序，版本在前的为母包，版本在后的的为补丁
                var group = groupTargets[idx].OrderBy(iO => iO.Version).ToList();
                var tCount = group.Count;
                if(0 >= tCount) continue;

                IDownloadInfo curOriginTarget = null;
                IDownloadInfo lastTarget = null;
                for (var tIdx = 0; tIdx < tCount; ++tIdx)
                {
                    var target = group[tIdx];
                    if(null == target || !target.Valid) continue;

                    if (null == curOriginTarget)
                    {
                        curOriginTarget = target;
                    }
                    else
                    {
                        lastTarget.SetPatch(target);
                    }

                    lastTarget = target;
                }

                if (null != curOriginTarget && curOriginTarget.Valid)
                {
                    _downloadTargets.Add(curOriginTarget);
                }
            }
            
            // 排序
            _downloadTargets = _downloadTargets
                .OrderBy(iO => iO.TotalCount)
                .ThenBy(iO => iO.TotalSize)
                .ToList();
            
            TotalCount = _downloadTargets.Sum(iO => iO.TotalCount);
            TotalSize = _downloadTargets.Sum(iO => iO.TotalSize);
            return 0 < TotalCount && 0 < TotalSize;
        }

        /// <summary>
        /// 取得下一个有效下载对象
        /// </summary>
        /// <returns>下一个有效下载对象</returns>
        private IDownloadInfo GetNextDownloadTarget()
        {
            if (null == _downloadTargets || 0 >= _downloadTargets.Count)
            {
                Warning("There is no target left for download!");
                return null;
            }

            IDownloadInfo downloadTarget = null;
            var count = _downloadTargets.Count;
            for (var idx = 0; idx < count; ++idx)
            {
                var target = _downloadTargets[idx];
                if(null == target || !target.Valid || target.Downloading || target.Downloaded) continue;

                downloadTarget = target;
                break;
            }

            return downloadTarget;
        }

        /// <summary>
        /// 状态
        /// </summary>
        public DownloadState State { get; private set; } = DownloadState.Ok;

        /// <summary>
        /// 状态描述
        /// </summary>
        public string StateDes { get; private set; } = null;
        
        /// <summary>
        /// 线程上限数
        /// <para>* 后台同时开启多少个下载线程</para>
        /// <para>* 默认:10</para>
        /// </summary>
        public int ThreaderMaxNun => Downloaders?.Length ?? 10;

        /// <summary>
        /// 运行中的子线程数
        /// </summary>
        public int RunningChildThreaderCount
        {
            get
            {
                if (null == Downloaders || 0 >= Downloaders.Length) return 0;
                return Downloaders.Count(iO => iO.Executing);
            }
        }
        
        /// <summary>
        /// 已经完成的下载个数
        /// </summary>
        public int DownloadedCount { get; private set; } = 0;

        /// <summary>
        /// 下载耗时
        /// <para>单位:毫秒</para>
        /// </summary>
        public long DownloadCostTime { get; private set; } = 0L;

        /// <summary>
        /// 下载总数
        /// </summary>
        public int TotalCount { get; private set; } = 0;

        /// <summary>
        /// 完成下载大小
        /// <para>* 单位 : Byte</para>
        /// </summary>
        public long DownloadedSize { get; private set; } = 0L;

        /// <summary>
        /// 下载总大小
        /// <para>* 单位 : Byte</para>
        /// </summary>
        public long TotalSize { get; private set; } = 0L;
        
        /// <summary>
        /// 下载速度
        /// </summary>
        public DownloadSpeed Speed { get; private set; } = null;

#endregion

#region Downloader

        /// <summary>
        /// 下载器列表
        /// </summary>
        public IDownloader[] Downloaders { get; private set; } = null;

        /// <summary>
        /// 空闲下载器数量
        /// </summary>
        public int IdleDownloaderCount { get; private set; } = 0;
        
        /// <summary>
        /// 计算空闲下载器数量
        /// </summary>
        /// <returns>空闲下载器数量</returns>
        private int CalcIdleDownloaderCount()
        {
            if (null == Downloaders || 0 >= Downloaders.Length)
            {
                Error("GetIdleDownloaderCount():There is no downloader left!");
                return 0;
            }

            var idleCount = 0;
            var count = Downloaders.Length;
            for (var idx = 0; idx < count; ++idx)
            {
                var downloader = Downloaders[idx];
                if(null == downloader || downloader.Executing) continue;
                ++idleCount;
            }

            if (0 >= idleCount)
            {
                //Warning("CalcIdleDownloaderCount():There is no idle downloader left!");
            }
            return idleCount;
        }

        /// <summary>
        /// 取得下一个空闲下载器
        /// </summary>
        /// <returns>空闲下载器</returns>
        private IDownloader GetNextIdleDownloader()
        {
            if (null == Downloaders || 0 >= Downloaders.Length)
            {
                Error("GetNextIdleDownloader():There is no downloader left!");
                return null;
            }

            IDownloader target = null;
            var count = Downloaders.Length;
            for (var idx = 0; idx < count; ++idx)
            {
                var downloader = Downloaders[idx];
                if(null == downloader || downloader.Executing) continue;

                target = downloader;
                break;
            }
            
            if (null == target)
            {
                Warning("GetNextIdleDownloader():There is no idle downloader left!");
            }
            return target;
        }

#endregion

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="iRetry">重试次数(默认:3)</param>
        /// <param name="iThreaderMaxNum">线程上限数(默认:5)</param>
        /// <param name="iMoveFile">移动文件标志位</param>
        /// <param name="iOptions">下载选项</param>
        /// <param name="iDecompressed">解压/解密回调</param>
        private void Init(int iRetry = 3, int iThreaderMaxNum = 5, bool iMoveFile = false, 
            int iOptions = (int)DownloadOptions.ResFullDownload, DownloadDecompressed iDecompressed = null)
        {
            // 已经初始化过了
            if(Initialized) return;
            
            // 初始化下载器列表
            Downloaders = new IDownloader[iThreaderMaxNum];
            var count = iThreaderMaxNum;
            for (var idx = 0; idx < count; ++idx)
            {
                Downloaders[idx] = Downloader.Create(idx, OnDownloaderCompleted, iOptions, iDecompressed, iRetry, iMoveFile);
            }
            
            // 默认初始化
            base.Init();
        }

#region Operate

        /// <summary>
        /// 取消下载
        /// <para>* 人为取消下载。</para>
        /// </summary>
        public override void Cancel()
        {
            // 通知正在下载的线程，取消下载
            NotifyCancel();
            
            base.Cancel();
        }

        /// <summary>
        /// 通知正在下载的线程，取消下载
        /// </summary>
        private void NotifyCancel()
        {
            if(null == Downloaders || 0 >= Downloaders.Length) return;
            
            // 遍历所有下载器
            var count = Downloaders.Length;
            for (var idx = 0; idx < count; ++idx)
            {
                var downloader = Downloaders[idx];
                downloader?.Cancel();
            }
        }

        /// <summary>
        /// 中断下载
        /// <para>* 发生异常时，内部逻辑中断下载</para>
        /// </summary>
        public override void Abort()
        {
            // 通知正在下载的线程，中断下载
            NotifyAbort();
            base.Abort();
        }
        
        /// <summary>
        /// 通知正在下载的线程，中断下载
        /// </summary>
        private void NotifyAbort()
        {
            if(null == Downloaders || 0 >= Downloaders.Length) return;
            
            // 遍历所有下载器
            var count = Downloaders.Length;
            for (var idx = 0; idx < count; ++idx)
            {
                var downloader = Downloaders[idx];
                downloader?.Abort();
            }
        }

        /// <summary>
        /// 暂停下载
        /// <para>* 与恢复下载，成对使用</para>
        /// </summary>
        public override void Pause()
        {
            // 通知正在下载的线程，暂停下载
            NotifyPause();
            base.Pause();
        }
        
        /// <summary>
        /// 通知正在下载的线程，暂停下载
        /// </summary>
        private void NotifyPause()
        {
            if(null == Downloaders || 0 >= Downloaders.Length) return;
            
            // 遍历所有下载器
            var count = Downloaders.Length;
            for (var idx = 0; idx < count; ++idx)
            {
                var downloader = Downloaders[idx];
                downloader?.Pause();
            }
        }

        /// <summary>
        /// 恢复下载
        /// </summary>
        public override void Resume()
        {
            // 通知暂停中的线程，恢复下载
            NotifyResume();
            base.Resume();
        }
        
        /// <summary>
        /// 通知暂停中的线程，恢复下载
        /// </summary>
        private void NotifyResume()
        {
            if(null == Downloaders || 0 >= Downloaders.Length) return;
            
            // 遍历所有下载器
            var count = Downloaders.Length;
            for (var idx = 0; idx < count; ++idx)
            {
                var downloader = Downloaders[idx];
                downloader?.Resume();
            }
        }
        
#endregion

#region Download

        /// <summary>
        /// 开始下载
        /// </summary>
        /// <param name="iTargets">下载目标列表</param>
        /// <param name="iProgress">下载进度回调</param>
        /// <param name="iFailed">下载失败回调</param>
        public void StartDownload(IList<IDownloadInfo> iTargets, 
            DownloadProgress iProgress = null, DownloadFailed iFailed = null)
        {
            if (!InitDownloadTargets(iTargets))
            {
                Error("StartDownload():InitDownloadQueue():Failed!");
                return;
            }

            _completedList.Clear();
            Speed = DownloadSpeed.Create(UtilsTime.GetCurDateTimeTicks(), ThreaderMaxNun);
            // 事件设定
            Progress = iProgress;
            Failed = iFailed;
            
            // 启动下载管理器线程(即：主控线程)
            Execute();
        }
        
        /// <summary>
        /// 执行前预处理
        /// <para>* 挂起睡眠等待时间间隔，默认值:500毫秒</para>
        /// </summary>
        /// <returns>true:待机，进入睡眠状态; false:中止待机，继续向下执行;</returns>
        protected override bool PrevExecute()
        {
            if (base.PrevExecute()) return true;
            // 若当前没有空闲下载器，则挂起，进入睡眠状态
            IdleDownloaderCount = CalcIdleDownloaderCount();

            var waiting = 0 >= IdleDownloaderCount;
            // 待机等待之前，刷新一次当前状态
            if (waiting)
            {
                Refresh();
            }
            return waiting;
        }

        /// <summary>
        /// 线程当前处理
        /// </summary>
        /// <returns>true:执行成功; false:执行失败;</returns>
        protected override bool CurExecute()
        {
            var flg = true;
            var downloaderCount = IdleDownloaderCount;
            for (var idx = 0; idx < downloaderCount; ++idx)
            {
                var downloader = GetNextIdleDownloader();
                if(null == downloader) continue;
                
                // 取得下一个要下载的资源信息
                var nextDstTarget = GetNextDownloadTarget();
                if (null == nextDstTarget || !nextDstTarget.Valid)
                {
                    // Warning("CurExecute():There is no target to download!");
                    break;
                }

                // 开始下载
                flg = downloader.Start(nextDstTarget);
                if (!flg)
                {
                    Error("CurExecute():Downloader Start Failed!(Downloader(Index:{0} State:{1} Desc:{2})\nTarget:{3})",
                        downloader.Index, downloader.State, downloader.StateDesc, nextDstTarget.ToString());
                    break;
                }
            }
            
            // 刷新
            Refresh();
            
            return flg;
        }

        /// <summary>
        /// 执行后处理
        /// </summary>
        /// <param name="iSucceed">执行成功标志位</param>
        /// <returns>true:继续执行; false:不用继续执行;</returns>
        protected override bool NextExecute(bool iSucceed)
        {
            if (!iSucceed || !base.NextExecute(true)) return false;

            // 已经执行失败，则终止执行，其余子线程，则走自己生命周期，自然结束
            if (DownloadState.Ok != State) return false;
            
            // 只要还有下载目标未下载完成，则继续执行
            var nextTarget = GetNextDownloadTarget();
            if (null != nextTarget) return true;
            
            // 刷新当前信息
            Refresh();
            return DownloadedSize != TotalSize;
        }

        private List<string> _completedList = new List<string>();

        /// <summary>
        /// 刷新
        /// </summary>
        private void Refresh()
        {
            // 刷新当前信息
            var count = _downloadTargets.Count;

            var curDownloadedCount = 0;
            var curDownloadedSize = 0L;

            IDownloadInfo lastDownloaded = null;
            for (var idx = 0; idx < count; ++idx)
            {
                var target = _downloadTargets[idx];
                if(null == target || !target.Valid) continue;

                curDownloadedCount += target.DownloadedCount;
                curDownloadedSize += target.DownloadedSize;
                
                if (target.AllDownloaded && !_completedList.Exists(iO => iO.Equals(target.FileName)))
                {
                    lastDownloaded = target;
                }
            }
            
            // 回调进度信息
            Progress?.Invoke(curDownloadedCount, TotalCount, 
                curDownloadedSize, TotalSize, CostTime, lastDownloaded);

            DownloadedCount = curDownloadedCount;
            DownloadedSize = curDownloadedSize;
            Thread.Sleep(100);
        }
        
#endregion

        /// <summary>
        /// 线程正常结束
        /// </summary>
        protected override void OnFinished()
        {
            // Error("OnFinished()：-----------------------------");
            // 强制回收GC一次
            GC.Collect();
        }

        /// <summary>
        /// 释放
        /// </summary>
        public override void Dispose()
        {
            base.Dispose();

            if (null != Downloaders && 0 < Downloaders.Length)
            {
                var count = Downloaders.Length;
                for (var idx = 0; idx < count; ++idx)
                {
                    var downloader = Downloaders[idx];
                    if(null == downloader) continue;
                
                    downloader.Stop();
                }
                Downloaders = null;
            }
            
            TotalCount = 0;
            TotalSize = 0L;
            
            _downloadTargets.Clear();
            _completedList.Clear();
            
        }
    }
}
